/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.bpm.engine.impl.persistence.entity.data.impl;

import com.je.bpm.engine.ProcessEngineConfiguration;
import com.je.bpm.engine.impl.EventSubscriptionQueryImpl;
import com.je.bpm.engine.impl.Page;
import com.je.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import com.je.bpm.engine.impl.persistence.CachedEntityMatcher;
import com.je.bpm.engine.impl.persistence.entity.*;
import com.je.bpm.engine.impl.persistence.entity.data.AbstractDataManager;
import com.je.bpm.engine.impl.persistence.entity.data.EventSubscriptionDataManager;
import com.je.bpm.engine.impl.persistence.entity.data.impl.cachematcher.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 */
public class MybatisEventSubscriptionDataManager extends AbstractDataManager<EventSubscriptionEntity> implements EventSubscriptionDataManager {

    private static List<Class<? extends EventSubscriptionEntity>> ENTITY_SUBCLASSES = new ArrayList<Class<? extends EventSubscriptionEntity>>();

    static {
        ENTITY_SUBCLASSES.add(MessageEventSubscriptionEntityImpl.class);
        ENTITY_SUBCLASSES.add(SignalEventSubscriptionEntityImpl.class);
        ENTITY_SUBCLASSES.add(CompensateEventSubscriptionEntityImpl.class);
    }

    protected CachedEntityMatcher<EventSubscriptionEntity> eventSubscriptionsByNameMatcher = new EventSubscriptionsByNameMatcher();

    protected CachedEntityMatcher<EventSubscriptionEntity> eventSubscritionsByExecutionIdMatcher = new EventSubscriptionsByExecutionIdMatcher();

    protected CachedEntityMatcher<EventSubscriptionEntity> eventSubscriptionsByProcInstTypeAndActivityMatcher = new EventSubscriptionsByProcInstTypeAndActivityMatcher();

    protected CachedEntityMatcher<EventSubscriptionEntity> eventSubscriptionsByExecutionAndTypeMatcher = new EventSubscriptionsByExecutionAndTypeMatcher();

    protected CachedEntityMatcher<EventSubscriptionEntity> signalEventSubscriptionByNameAndExecutionMatcher = new SignalEventSubscriptionByNameAndExecutionMatcher();

    protected CachedEntityMatcher<EventSubscriptionEntity> signalEventSubscriptionByProcInstAndEventNameMatcher = new SignalEventSubscriptionByProcInstAndEventNameMatcher();

    protected CachedEntityMatcher<EventSubscriptionEntity> signalEventSubscriptionByEventNameMatcher = new SignalEventSubscriptionByEventNameMatcher();

    protected CachedEntityMatcher<EventSubscriptionEntity> messageEventSubscriptionsByProcInstAndEventNameMatcher = new MessageEventSubscriptionsByProcInstAndEventNameMatcher();

    public MybatisEventSubscriptionDataManager(ProcessEngineConfigurationImpl processEngineConfiguration) {
        super(processEngineConfiguration);
    }

    @Override
    public Class<? extends EventSubscriptionEntity> getManagedEntityClass() {
        return EventSubscriptionEntityImpl.class;
    }

    @Override
    public List<Class<? extends EventSubscriptionEntity>> getManagedEntitySubClasses() {
        return ENTITY_SUBCLASSES;
    }

    @Override
    public EventSubscriptionEntity create() {
        // only allowed to create subclasses
        throw new UnsupportedOperationException();
    }

    @Override
    public CompensateEventSubscriptionEntity createCompensateEventSubscription() {
        return new CompensateEventSubscriptionEntityImpl();
    }

    @Override
    public MessageEventSubscriptionEntity createMessageEventSubscription() {
        return new MessageEventSubscriptionEntityImpl();
    }

    @Override
    public SignalEventSubscriptionEntity createSignalEventSubscription() {
        return new SignalEventSubscriptionEntityImpl();
    }

    @Override
    public long findEventSubscriptionCountByQueryCriteria(EventSubscriptionQueryImpl eventSubscriptionQueryImpl) {
        final String query = "selectEventSubscriptionCountByQueryCriteria";
        return (Long) getDbSqlSession().selectOne(query, eventSubscriptionQueryImpl);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<EventSubscriptionEntity> findEventSubscriptionsByQueryCriteria(EventSubscriptionQueryImpl eventSubscriptionQueryImpl, Page page) {
        final String query = "selectEventSubscriptionByQueryCriteria";
        return getDbSqlSession().selectList(query, eventSubscriptionQueryImpl, page);
    }

    @Override
    public List<MessageEventSubscriptionEntity> findMessageEventSubscriptionsByProcessInstanceAndEventName(final String processInstanceId, final String eventName) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("processInstanceId", processInstanceId);
        params.put("eventName", eventName);
        return toMessageEventSubscriptionEntityList(getList("selectMessageEventSubscriptionsByProcessInstanceAndEventName",
                params, messageEventSubscriptionsByProcInstAndEventNameMatcher, true));
    }

    @Override
    public List<SignalEventSubscriptionEntity> findSignalEventSubscriptionsByEventName(final String eventName, final String tenantId) {
        final String query = "selectSignalEventSubscriptionsByEventName";

        final Map<String, String> params = new HashMap<String, String>();
        params.put("eventName", eventName);
        if (tenantId != null && !tenantId.equals(ProcessEngineConfiguration.NO_TENANT_ID)) {
            params.put("tenantId", tenantId);
        }

        List<EventSubscriptionEntity> result = getList(query, params, signalEventSubscriptionByEventNameMatcher, true);
        return toSignalEventSubscriptionEntityList(result);
    }

    @Override
    public List<SignalEventSubscriptionEntity> findSignalEventSubscriptionsByProcessInstanceAndEventName(final String processInstanceId, final String eventName) {
        final String query = "selectSignalEventSubscriptionsByProcessInstanceAndEventName";
        Map<String, String> params = new HashMap<String, String>();
        params.put("processInstanceId", processInstanceId);
        params.put("eventName", eventName);
        return toSignalEventSubscriptionEntityList(getList(query, params, signalEventSubscriptionByProcInstAndEventNameMatcher, true));
    }

    @Override
    public List<SignalEventSubscriptionEntity> findSignalEventSubscriptionsByNameAndExecution(final String name, final String executionId) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("executionId", executionId);
        params.put("eventName", name);
        return toSignalEventSubscriptionEntityList(getList("selectSignalEventSubscriptionsByNameAndExecution", params, signalEventSubscriptionByNameAndExecutionMatcher, true));
    }

    @Override
    public List<EventSubscriptionEntity> findEventSubscriptionsByExecutionAndType(final String executionId, final String type) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("executionId", executionId);
        params.put("eventType", type);
        return getList("selectEventSubscriptionsByExecutionAndType", params, eventSubscriptionsByExecutionAndTypeMatcher, true);
    }

    @Override
    public List<EventSubscriptionEntity> findEventSubscriptionsByProcessInstanceAndActivityId(final String processInstanceId, final String activityId, final String type) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("processInstanceId", processInstanceId);
        params.put("eventType", type);
        params.put("activityId", activityId);
        return getList("selectEventSubscriptionsByProcessInstanceTypeAndActivity", params, eventSubscriptionsByProcInstTypeAndActivityMatcher, true);
    }

    @Override
    public List<EventSubscriptionEntity> findEventSubscriptionsByExecution(final String executionId) {
        return getList("selectEventSubscriptionsByExecution", executionId, eventSubscritionsByExecutionIdMatcher, true);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<EventSubscriptionEntity> findEventSubscriptionsByTypeAndProcessDefinitionId(String type, String processDefinitionId, String tenantId) {
        final String query = "selectEventSubscriptionsByTypeAndProcessDefinitionId";
        Map<String, String> params = new HashMap<String, String>();
        if (type != null) {
            params.put("eventType", type);
        }
        params.put("processDefinitionId", processDefinitionId);
        if (tenantId != null && !tenantId.equals(ProcessEngineConfiguration.NO_TENANT_ID)) {
            params.put("tenantId", tenantId);
        }
        return getDbSqlSession().selectList(query, params);
    }

    @Override
    public List<EventSubscriptionEntity> findEventSubscriptionsByName(final String type, final String eventName, final String tenantId) {

        Map<String, String> params = new HashMap<String, String>();
        params.put("eventType", type);
        params.put("eventName", eventName);
        if (tenantId != null && !tenantId.equals(ProcessEngineConfiguration.NO_TENANT_ID)) {
            params.put("tenantId", tenantId);
        }

        return getList("selectEventSubscriptionsByName", params, eventSubscriptionsByNameMatcher, true);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<EventSubscriptionEntity> findEventSubscriptionsByNameAndExecution(String type, String eventName, String executionId) {
        final String query = "selectEventSubscriptionsByNameAndExecution";
        Map<String, String> params = new HashMap<String, String>();
        params.put("eventType", type);
        params.put("eventName", eventName);
        params.put("executionId", executionId);
        return getDbSqlSession().selectList(query, params);
    }

    @Override
    public MessageEventSubscriptionEntity findMessageStartEventSubscriptionByName(String messageName, String tenantId) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("eventName", messageName);
        if (tenantId != null && !tenantId.equals(ProcessEngineConfiguration.NO_TENANT_ID)) {
            params.put("tenantId", tenantId);
        }
        MessageEventSubscriptionEntity entity = (MessageEventSubscriptionEntity) getDbSqlSession().selectOne("selectMessageStartEventSubscriptionByName", params);
        return entity;
    }

    @Override
    public void updateEventSubscriptionTenantId(String oldTenantId, String newTenantId) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("oldTenantId", oldTenantId);
        params.put("newTenantId", newTenantId);
        getDbSqlSession().update("updateTenantIdOfEventSubscriptions", params);
    }

    @Override
    public void deleteEventSubscriptionsForProcessDefinition(String processDefinitionId) {
        getDbSqlSession().delete("deleteEventSubscriptionsForProcessDefinition", processDefinitionId, EventSubscriptionEntityImpl.class);
    }

    protected List<SignalEventSubscriptionEntity> toSignalEventSubscriptionEntityList(List<EventSubscriptionEntity> result) {
        List<SignalEventSubscriptionEntity> signalEventSubscriptionEntities = new ArrayList<SignalEventSubscriptionEntity>(result.size());
        for (EventSubscriptionEntity eventSubscriptionEntity : result) {
            signalEventSubscriptionEntities.add((SignalEventSubscriptionEntity) eventSubscriptionEntity);
        }
        return signalEventSubscriptionEntities;
    }

    protected List<MessageEventSubscriptionEntity> toMessageEventSubscriptionEntityList(List<EventSubscriptionEntity> result) {
        List<MessageEventSubscriptionEntity> messageEventSubscriptionEntities = new ArrayList<MessageEventSubscriptionEntity>(result.size());
        for (EventSubscriptionEntity eventSubscriptionEntity : result) {
            messageEventSubscriptionEntities.add((MessageEventSubscriptionEntity) eventSubscriptionEntity);
        }
        return messageEventSubscriptionEntities;
    }

}
